package com.yuanluludev.lib_logcat.crash

import android.app.Application
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import android.os.Looper
import android.os.Process
import android.text.TextUtils
import android.view.WindowManager.BadTokenException
import android.widget.Toast
import androidx.annotation.NonNull
import androidx.core.app.ActivityCompat
import androidx.work.Data
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import java.io.PrintWriter
import java.io.StringWriter
import java.text.SimpleDateFormat
import java.util.*
import kotlin.system.exitProcess


class CrashHandler : Thread.UncaughtExceptionHandler {
    companion object {
        private val INSTANCE = CrashHandler()

        /**
         * 注册 Crash 监听
         */
        fun getInstance(): CrashHandler {
            return INSTANCE
        }

    }

    private var mApplication: Application? = null
    private var mOldHandler: Thread.UncaughtExceptionHandler? = null
    var isUpload = false
    fun init(application: Application, isUpload: Boolean) {
        mApplication = application
        mOldHandler = Thread.getDefaultUncaughtExceptionHandler()
        this.isUpload = isUpload
        Thread.setDefaultUncaughtExceptionHandler(this)
        check(javaClass.name != mOldHandler!!.javaClass.name) {
            // 请不要重复注册 Crash 监听
            "are you ok?"
        }
    }

    override fun uncaughtException(@NonNull thread: Thread?, @NonNull throwable: Throwable) {
        if (!isUpload) restart(thread, throwable)
        else exit(throwable)
    }

    private fun restart(thread: Thread?, throwable: Throwable) {
        CrashActivity.start(mApplication, initData(throwable))
        if (mOldHandler != null && !mOldHandler!!.javaClass.name.startsWith("com.android.internal.os")) {
            mOldHandler!!.uncaughtException(thread, throwable)
        }
        Process.killProcess(Process.myPid())
        exitProcess(10)
    }

    private fun exit(throwable: Throwable) {
        object : Thread() {
            override fun run() {
                // Toast 显示需要出现在一个线程的消息队列中
                Looper.prepare()
                Toast.makeText(mApplication, "程序异常，请重新打开", Toast.LENGTH_LONG)
                    .show()
                Looper.loop()
            }
        }.start()
        // Sleep一会后结束程序
        // 来让线程停止一会是为了显示Toast信息给用户，然后Kill程序
        try {
            if (mApplication != null) {
                val putData = Data.Builder().putString("msg", initData(throwable!!)).build()
                val uploader = OneTimeWorkRequest.Builder(UploadCatchUncaughtWorker::class.java)
                    .setInputData(putData).build()
                WorkManager.getInstance(mApplication!!).enqueue(uploader)
            }
            Thread.sleep(3000)
        } catch (e: InterruptedException) {
        }
        // 杀死进程（这个事应该是系统干的，但是它会多弹出一个崩溃对话框，所以需要我们自己手动杀死进程）
        Process.killProcess(Process.myPid())
        exitProcess(10)
    }

    /** 外部存储权限（特殊权限，需要 Android 11 及以上）  */
    private val MANAGE_EXTERNAL_STORAGE = "android.permission.MANAGE_EXTERNAL_STORAGE"

    /** 应用安装权限（特殊权限，需要 Android 8.0 及以上）  */
    private val REQUEST_INSTALL_PACKAGES = "android.permission.REQUEST_INSTALL_PACKAGES"

    /** 通知栏权限（特殊权限，需要 Android 7.0 及以上）  */
    private val NOTIFICATION_SERVICE = "android.permission.ACCESS_NOTIFICATION_POLICY"

    /** 悬浮窗权限（特殊权限，需要 Android 6.0 及以上）  */
    private val SYSTEM_ALERT_WINDOW = "android.permission.SYSTEM_ALERT_WINDOW"

    /** 修改系统设置权限（特殊权限，需要 Android 6.0 及以上）  */
    private val WRITE_SETTINGS = "android.permission.WRITE_SETTINGS"


    /** 拍照权限  */
   private val CAMERA = "android.permission.CAMERA"

    private fun initData(throwable: Throwable): String {
        val stringWriter = StringWriter()
        val printWriter = PrintWriter(stringWriter)
        throwable.printStackTrace(printWriter)
        val builder = StringBuilder()
        if (throwable is NullPointerException) {
            builder.append("空指针异常")
        } else if (throwable is ClassCastException) {
            builder.append("类型转换异常")
        } else if (throwable is ActivityNotFoundException) {
            builder.append("活动跳转异常")
        } else if (throwable is IllegalArgumentException) {
            builder.append("非法参数异常")
        } else if (throwable is IllegalStateException) {
            builder.append("非法状态异常")
        } else if (throwable is BadTokenException) {
            builder.append("窗口添加异常")
        } else if (throwable is StackOverflowError) {
            builder.append("栈溢出")
        } else if (throwable is OutOfMemoryError) {
            builder.append("内存溢出")
        }
        builder.append("\n设备品牌：").append(Build.BRAND)
            .append("\t设备型号：").append(Build.MODEL)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            builder.append("\n安卓版本：").append(Build.VERSION.RELEASE)
                .append("\tSDK\t版本：").append(Build.VERSION.SDK_INT)
                .append("\tCPU\t架构：").append(Build.SUPPORTED_ABIS[0])
        }
        builder.append("\n版本代码：\t").append(getAppVersionName(mApplication!!))
        try {
            val packageInfo = mApplication!!.packageManager.getPackageInfo(
                mApplication!!.packageName,
                PackageManager.GET_PERMISSIONS
            )
            val dateFormat = SimpleDateFormat("yy-MM-dd HH:mm")
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
                builder.append("\n首次安装：\t")
                    .append(dateFormat.format(Date(packageInfo.firstInstallTime)))
                    .append("\t最近安装：\t").append(dateFormat.format(Date(packageInfo.lastUpdateTime)))
                    .append("\t崩溃时间：\t").append(dateFormat.format(Date()))
            }
            val permissions = packageInfo.requestedPermissions

            if (permissions.contains(MANAGE_EXTERNAL_STORAGE) || permissions.contains(MANAGE_EXTERNAL_STORAGE)) {
                builder.append("\n存储权限：\t")
                if (hasPermission(mApplication!!, MANAGE_EXTERNAL_STORAGE)) {
                    builder.append("读、写")
                } else {
                    if (hasPermission(
                            mApplication!!,
                            MANAGE_EXTERNAL_STORAGE
                        )
                    ) {
                        builder.append("读")
                    } else if (hasPermission(
                            mApplication!!,
                            MANAGE_EXTERNAL_STORAGE
                        )
                    ) {
                        builder.append("写")
                    } else {
                        builder.append("未获得")
                    }
                }
            }
            if (permissions.contains(CAMERA)) {
                builder.append("\t相机权限：\t").append(
                    if (hasPermission(
                            mApplication!!,
                            CAMERA
                        )
                    ) "已获得" else "未获得"
                )
            }
            if (permissions.contains(REQUEST_INSTALL_PACKAGES)) {
                builder.append("\t安装包权限：\t").append(
                    if (hasPermission(
                            mApplication!!,
                            REQUEST_INSTALL_PACKAGES
                        )
                    ) "已获得" else "未获得"
                )
            }
        } catch (ignored: PackageManager.NameNotFoundException) {
        }
        builder.append("\n" + stringWriter.toString());
        return builder.toString()
    }

    private fun hasPermission(context: Context, permission: String): Boolean {
        return ActivityCompat.checkSelfPermission(
            context,
            permission
        ) == PackageManager.PERMISSION_GRANTED
    }

    private fun getAppVersionName(context: Context): String? {
        val packageName: String = context.packageName
        return if (TextUtils.isEmpty(packageName)) {
            null
        } else {
            try {
                val pm: PackageManager = context.getPackageManager()
                val pi = pm.getPackageInfo(packageName, 0)
                pi?.versionName
            } catch (var4: PackageManager.NameNotFoundException) {
                var4.printStackTrace()
                null
            }
        }
    }
}